/*
@Time : 2019/7/11 19:45
@Author : Leixiaotian
@File : jwt_middleware.go
@Software: GoLand
*/
package middlewares

import (
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	"io"
	"log"
	"micro-bbs/initialize"
	"net/http"
	"micro-bbs/libs"
	"strconv"
	"strings"
	"time"
)

//注册请求头 用于判断用户是否登录
type HeaderUser struct {
	Uid       int64  `json:"uid"`
	UserLevel int    `json:"userLevel"`
	Ticket    string `json:"ticket"`
	NickName  string `json:"nickName"`
	Mobile    string `json:"mobile"`
	HeadImg   string `json:"headImg"`
}

// JWT 签名结构
type JWT struct {
	SigningKey []byte
}

// 一些常量
var (
	TokenExpired     error  = errors.New("Token is expired")
	TokenNotValidYet error  = errors.New("Token not active yet")
	TokenMalformed   error  = errors.New("That's not even a token")
	TokenInvalid     error  = errors.New("Couldn't handle this token:")
	SignKey          string = fmt.Sprintf(`-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjmgfejBXsPYynAIPczHA
eEvTDfAVaNKQudyI7VszdezbHDM1CStCIgwiMmLd7QYf1SrrmQoqxhcSRbhjE3ej
RF5qzhtx3kmepdpMrQptcsLjRkixaxCc4E2k6Us5707gGwbhoaTrRit5F2MnAdLY
C1TS3WwnO/hQfqUcAglbK8yrJ4AwAv0DAoIUSWnWqzuniV1SYbdV57uswxUssoWy
sEfPz+nv1ZLRs6Wz4eQ5Myqx2+CjWc9F8iXa2PV8Rmjms3dVbWcLUpCP18Dfzp8l
n8vF9LfYB7UaLSpfJe6FFF6+vCg4JHfo12djTUgwGjauMF3e9mmjU83KIoQS66lp
AQIDAQAB
-----END PUBLIC KEY-----`)
)

// 载荷，可以加一些自己需要的信息
type CustomClaims struct {
	Exp      int    `json:"exp"`
	UserName string `json:"user_name"`
	ClientId string `json:"client_id"`
	Jti      string `json:"jti"`
	jwt.StandardClaims
}

// 新建一个jwt实例
func NewJWT() *JWT {
	return &JWT{
		[]byte(GetSignKey()),
	}
}

// 获取signKey
func GetSignKey() string {
	return SignKey
}
//解析token
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			log.Panicln("unexpected signing method")
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}
		return j.SigningKey, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, TokenInvalid
}

func JwtMiddleware() gin.HandlerFunc {

	return func(c *gin.Context) {

		ignoreUrl := []string{"/v2/gifts", "/v2/vips", "/v2/anchors/list/commissions", "/v2/anchors/divide", "/v2/anchors/room/rank", "/v2/anchors/rank"}
		for _, val := range ignoreUrl {
			if strings.Contains(c.Request.URL.Path, val) {
				c.Next()
				return
			}
		}
		//增加特殊接口过滤
		if c.Request.URL.Path == "/v2/anchors" {
			c.Next()
			return
		}

		jwtToken := c.Request.Header.Get("Authorization")
		if jwtToken == "" {
			c.AbortWithStatusJSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "token不能为空",
			})
			return
		}
		token := jwtToken
		//判断是否存在Bearer
		hasBearer := strings.Contains(jwtToken, "Bearer")
		if hasBearer {
			tokenArray := strings.Split(jwtToken, " ")
			token = tokenArray[1]
		}

		// parseToken 解析token包含的信息
		j := NewJWT()
		jwtInfo, err := j.ParseToken(token)
		if err != nil {
			c.AbortWithStatusJSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "token解析失败",
			})
			return
		}
		//解析user_name
		//userInifo := jwtInfo.UserName
		var headerUser HeaderUser
		//err = json.Unmarshal([]byte(userInifo), &headerUser)
		//if err != nil {
		//	c.AbortWithStatusJSON(http.StatusOK, gin.H{
		//		"code": -1,
		//		"msg":  err.Error(),
		//	})
		//	return
		//}
		uid, err := strconv.ParseInt(jwtInfo.StandardClaims.Subject, 10, 64)
		headerUser.Uid = uid
		//传参usr_id
		c.Set("jwtUserId", headerUser.Uid)

		//防止重复提交
		timestamp := fmt.Sprintf("%d", time.Now().UnixNano()/1e6)
		nonce := fmt.Sprintf("%s", uuid.Must(uuid.NewV4(), nil))
		sign := timestamp + nonce
		w := md5.New()
		io.WriteString(w, sign)
		sign = fmt.Sprintf("%x", w.Sum(nil))
		c.Header("timestamp", timestamp)
		c.Header("nonce", nonce)
		c.Header("sign", sign)
		c.Set("timestamp", timestamp)
		c.Set("nonce", nonce)
		c.Set("sign", sign)

		c.Next()
	}

}

//用户中间件
func UserMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {

		//需要用户登录处理的path
		inUserPath := map[string]string{
			"/v2/members/beans/add": "beans_add",
		}

		if strings.Contains(c.Request.URL.Path, "/v2/cashs/add") {
			inUserPath[c.Request.URL.Path] = "cashs_add"
		}

		//如果存在需要判断的路径
		if _, ok := inUserPath[c.Request.URL.Path]; ok {
			//判断用户是否登录
			var header HeaderUser
			userHeader := c.GetHeader("x-user-header")
			if userHeader == "" {
				c.AbortWithStatusJSON(http.StatusOK, libs.ReturnJson(400209, "", nil))
				return
			}

			//解析x-user-header
			err := json.Unmarshal([]byte(userHeader), &header)
			if err != nil {
				//data.Log.WithField("Middleware:UserMiddleware", "解析x-user-header失败").Warn(err.Error())
				initialize.IrisLog.Errorf("[Middleware:UserMiddleware-解析x-user-header失败]-[%s]", err.Error())
				c.AbortWithStatusJSON(http.StatusOK, libs.ReturnJson(400209, "", nil))
				return
			}

			//GET
			if c.Request.Method == "GET" {
				queryUserId := c.Query("user_id")
				userId, _ := strconv.ParseInt(queryUserId, 10, 64)
				if userId != header.Uid {
					c.AbortWithStatusJSON(http.StatusOK, libs.ReturnJson(400210, "", nil))
					return
				}
			}
			//POST
			if c.Request.Method == "POST" {
				queryUserId := c.PostForm("user_id")
				userId, _ := strconv.ParseInt(queryUserId, 10, 64)
				if userId != header.Uid {
					c.AbortWithStatusJSON(http.StatusOK, libs.ReturnJson(400210, "", nil))
					return
				}
			}

		}

		//防止重复提交
		timestamp := fmt.Sprintf("%d", time.Now().UnixNano()/1e6)
		nonce := fmt.Sprintf("%s", uuid.Must(uuid.NewV4(), nil))
		sign := timestamp + nonce
		w := md5.New()
		io.WriteString(w, sign)
		sign = fmt.Sprintf("%x", w.Sum(nil))
		c.Header("timestamp", timestamp)
		c.Header("nonce", nonce)
		c.Header("sign", sign)

		c.Next()
	}
}
